﻿using System;
using System.Collections.Generic;

#if UNITY_EDITOR
using Sirenix.OdinInspector;
#endif

namespace Hsenl {
    // 数据版本管理, 主要用于处理旧数据与新数据之间的适配.
    // 例如游戏初期的玩家数据是v1, 后来由于更新, 玩家数据也发生了变化, v2, v3...
    // 这时, 应该首先把玩家的数据拿到这里更为最新版本, 然后再使用.
    // 管理器会从数据当前的版本开始, 由每个版本都处理一遍, 最终得到最新的数据.
    [Serializable]
    public class DataVersionManager<T> : Singleton<DataVersionManager<T>> where T : IDataVersionUpdatable {
#if UNITY_EDITOR
        [ShowInInspector, ReadOnly]
#endif
        private readonly SortedDictionary<int, IDataVersionHandler> _handlers = new();

        public int LatestVersion { get; private set; }

        protected override void OnRegister() {
            RegisterDataVersionHandlers();
        }

        public void RegisterDataVersionHandler(IDataVersionHandler handler) {
            // 版本处理, 不能降版本
            if (handler.InputVersion >= handler.OutputVersion)
                throw new Exception(
                    $"Output version must be less than to InputVersion. InputVersion:'{handler.InputVersion}' OutputVersion:'{handler.OutputVersion}'");

            this._handlers.Add(handler.InputVersion, handler);
        }

        public void UnregisterDataVersionHandler(IDataVersionHandler handler) {
            this._handlers.Remove(handler.InputVersion);
        }

        public IDataVersionUpdatable UpadateVersion(IDataVersionUpdatable dataUpdatable) {
            var data = dataUpdatable;
            var version = data.Version;
            foreach (var kv in this._handlers) {
                var handler = kv.Value;
                if (handler.InputVersion != version)
                    continue;

                data = handler.Handle(data);
                data.Version = handler.OutputVersion;
                if (data.Version != handler.OutputVersion) {
                    throw new Exception($"Output version mismatch. {data.GetType()} '{data.Version}' '{handler.OutputVersion}'");
                }

                version = handler.OutputVersion;
            }

            return data;
        }

        [OnEventSystemReload]
        private static void RegisterDataVersionHandlers() {
            if (Instance == null)
                return;

            Instance._handlers.Clear();

            var targetType = typeof(T);
            foreach (var type in AssemblyHelper.GetSubTypes(typeof(IDataVersionHandler), EventSystem.GetAssemblies())) {
                var handler = (IDataVersionHandler)Activator.CreateInstance(type);
                if (handler.TargetType != targetType) // 跳过目标类型不同的handler
                    continue;

                Instance.RegisterDataVersionHandler(handler);
            }

            var latestVersion = 0;
            foreach (var kv in Instance._handlers) {
                if (kv.Value.OutputVersion > latestVersion)
                    latestVersion = kv.Value.OutputVersion;
            }

            Instance.LatestVersion = latestVersion;
        }
    }
}